#ifndef ENTITY_MANAGER_HPP
#define ENTITY_MANAGER_HPP

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <unordered_set>

#include <SFML/Graphics/RenderWindow.hpp>

#include <SmegTools/Config.h>
#include <SmegTools/Pattern/Singleton.hpp>

#include "../Math/BoundingVolumes/BoundingVolume.hpp"

#include <SmegTools/Debug/DebugNew.hpp>

namespace smeg {
    namespace math {
        class BoundingBox;
        class QuadTree;
    }
    namespace core {
        
        class BaseObject;
        class BaseEntity;
        class MovableEntity;

////////////////////////////////////////////////////////////
/// Manage all object derived from MovableEntity
////////////////////////////////////////////////////////////
class EntityManager : public tools::Singleton< EntityManager >
{
    DECLARE_SINGLETON( EntityManager )

    typedef std::unordered_set<BaseEntity*>                   td_BaseEntitySet;
    typedef std::unordered_set<BaseEntity*>::iterator         td_BaseEntitySetIt;
    typedef std::unordered_set<BaseEntity*>::const_iterator   td_BaseEntitySetConstIt;

public:
    ////////////////////////////////////////////////////////////
    /// Initialize the manager
    ///
    /// \param _pRenderWindow : Render window
    ///
    ////////////////////////////////////////////////////////////
    void                        Init( sf::RenderWindow* _pRenderWindow );

    ////////////////////////////////////////////////////////////
    /// DeInit the manager
    ///
    ////////////////////////////////////////////////////////////
    void                        DeInit();
    
    ////////////////////////////////////////////////////////////
    /// Setup QuadTree partionning
    ///
    /// \param _Region : Region to have the quad tree
    ///
    ////////////////////////////////////////////////////////////
    void                        SetupQuadTree( const math::BoundingBox& _Region );

    ////////////////////////////////////////////////////////////
    /// Clear the scene by removing all entities
    ///
    ////////////////////////////////////////////////////////////
    void                        ClearScene();

    ////////////////////////////////////////////////////////////
    /// Update the entities
    ///
    /// \param _fDeltaTime :    Time since the last frame
    ///
    ////////////////////////////////////////////////////////////
    void                        UpdateEntities( float _fDeltaTime );

    ////////////////////////////////////////////////////////////
    /// Render the entities
    ///
    ////////////////////////////////////////////////////////////
    void                        RenderEntities();
    
    ////////////////////////////////////////////////////////////
    /// Add a entity, if the entity is already in the system, don't add it
    ///
    /// \param _pEntity :       Entity to add
    ///
    /// \return Whether the entity was successfully added or not
    ///
    ////////////////////////////////////////////////////////////
    bool                        AddEntity( BaseEntity* _pEntity );
    bool                        AddEntity( MovableEntity* _pEntity );

    ////////////////////////////////////////////////////////////
    /// Remove a entity
    ///
    /// \param _pEntity :       Entity to remove
    /// \param _bDeleteEntity : Whether to delete the entity after removing or not
    ///
    ////////////////////////////////////////////////////////////
    void                        RemoveEntity( BaseEntity* _pEntity, bool _bDeleteEntity = true );
    
    ////////////////////////////////////////////////////////////
    /// Query a region for entities
    ///
    /// \param _Region :        Region to check
    /// \param _Result :        Output vector of occupants
    ///
    ////////////////////////////////////////////////////////////
    void                        QueryStaticEntitiesRegion( const math::BoundingBox& _Region, std::vector<BaseObject*>& _Result, math::BoundingVolume::eBoundingVolume _eType = math::BoundingVolume::eAny ) const;
    void                        QueryMovableEntitiesRegion( const math::BoundingBox& _Region, std::vector<BaseObject*>& _Result, math::BoundingVolume::eBoundingVolume _eType = math::BoundingVolume::eAny ) const;

protected:    
    /******************* Protected members ********************/
    td_BaseEntitySet            m_Entities;

    sf::RenderWindow*           m_pRenderWindow;
    math::QuadTree*             m_pStaticEntitiesQuadTree;
    math::QuadTree*             m_pMovableEntitiesQuadTree;

#ifdef _DEBUG
public:
    void                        DebugRender( const sf::Color& _Color = sf::Color::White );
#endif // _DEBUG
};

    } // namespace core
} // namespace smeg

#include <SmegTools/Debug/DebugNewOff.hpp>

#endif // ENTITY_MANAGER_HPP
